<!DOCTYPE html>
<html class="no-js">
<head>
  <base href="http://www.flywithme.top//">
  <script>
    document._writeOriginal = document.write;
    document.write = function(str) {
        if (str.indexOf('livereload.js') > -1) {
            document._writeOriginal(str);
        } else {
            document._writeOriginal('<!-- Be Hijack!! -->');
        }
    }
  </script>
  <title>React Native如何做跨平台设计 - Fly With Me</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="keywords" content="React Native,">
<meta name="description" content="React Native ">
<meta name="author" content="brasbug">
<meta name="publisher" content="brasbug">
<meta name="generator" content="http://gohugo.io/"/>

<meta itemprop="name" content="React Native如何做跨平台设计 - Fly With Me">
<meta itemprop="description" content="React Native ">
<meta itemprop="image" content="http://www.flywithme.top//public/coderjk.jpg">

<meta property="og:title" content="React Native如何做跨平台设计 - Fly With Me" />
<meta property="og:type" content="article" />
<meta property="og:url" content="http://www.flywithme.top/2016/04/28/ReactNativeRD/" />
<meta property="og:image" content="http://www.flywithme.top//public/coderjk.jpg" />
<meta property="og:description" content="React Native ">

<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@brasbug">
<meta name="twitter:title" content="React Native如何做跨平台设计 - Fly With Me">
<meta name="twitter:description" content="React Native ">
<meta name="twitter:creator" content="@brasbug">
<meta name="twitter:image" content="http://www.flywithme.top//public/coderjk.jpg">

  <script>document.documentElement.className = document.documentElement.className.replace("no-js", "js");</script>
  <link rel="canonical" href="http://www.flywithme.top/2016/04/28/ReactNativeRD/">
  <link rel='shortlink' href="http://www.flywithme.top/2016/04/28/ReactNativeRD/"/>
  <link rel="shortcut icon" href="http://www.flywithme.top//public/coderjk.ico"/>
  
<link rel="stylesheet" id="human-style-css" href="http://www.flywithme.top/wp-content/themes/hueman/style.css" type="text/css" media="all"/>
<link rel="stylesheet" id="human-style-css2" href="http://www.flywithme.top/wp-content/themes/hueman-child/style.css" type="text/css" media="all"/>
<link rel="stylesheet" id="responsive-css" href="http://www.flywithme.top/wp-content/themes/hueman/responsive.css" type="text/css" media="all"/>
<link rel="stylesheet" id="font-awesome-css" href="http://www.flywithme.top/wp-content/themes/hueman/fonts/font-awesome.min.css" type="text/css" media="all"/>
<link rel="stylesheet" href="http://www.flywithme.top/public/highlight/styles/github.css">
<script src="http://www.flywithme.top/public/highlight/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<link rel="stylesheet" id="human-style-css3" href="http://www.flywithme.top/wp-content/themes/hueman-child/user.css" type="text/css" media="all"/>
<link rel="stylesheet" href="public/font/hack/css/hack.min.css">

<script type="text/javascript" src="assets/js/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="wp-content/themes/hueman/js/jquery.jplayer.min.js"></script>
<script type="text/javascript" src="wp-content/themes/hueman/js/scripts.js"></script>
<script type="text/javascript" src="assets/picturefill/picturefill.min.js"></script>
<script type="text/javascript" src="assets/js/jquery.matchHeight-min.js"></script>
<script type="text/javascript" async defer src="assets/js/myblog.js"></script>

</head>

<body class="single single-post single-format-standard col-3cm full-width topbar-enabled chrome">
<div id="wrapper">
  <header id="header">

  <nav class="nav-container group" id="nav-topbar">
    <div class="nav-toggle"><i class="fa fa-bars"></i></div>
    <div class="nav-text"></div>
    <div class="nav-wrap container">
      <a rel="nofollow" href="http://www.flywithme.top//" class="nav-cs-icon">
        <img width="40" height="40" src="http://www.flywithme.top/public/coderjk.jpg" alt="brasbug" title="Home">
      </a>
      <ul id="menu-default-menu" class="nav container-inner group">
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/">首页</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E6%8A%80%E6%9C%AF%E6%96%87%E7%AB%A0/">技术文章</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E6%80%9D%E8%80%83%E6%84%9F%E6%82%9F/">思考感悟</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
        </li>
        
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/post/">归档</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/about/">关于我</a>
        </li>
        
        
          
        
        <li class="menu-item menu-item-type-post_type menu-item-text">
        浪尖一滴水,引领一片潮
        </li>
      </ul>
    </div>

    <div class="container">
      <div class="container-inner">
        <div class="toggle-search"><i class="fa fa-search"></i></div>
        <div class="search-expand">
          <div class="search-expand-inner">
            <form method="get" class="searchform themeform" action="https://www.google.com/search">
              <div>
                <input type="text" class="search" name="q" placeholder="Press enter to start searching">
              </div>
            </form>
          </div>
        </div>
      </div>
      
    </div>
    

  </nav>
  

  <div class="container group">
    <div class="container-inner">

      <div class="group pad">

        <div class="group pad">
          <h1 class="site-title">
            <a rel="nofollow" href="http://www.flywithme.top//" rel="home">
              <img style="border-radius: 50%;width:72px;height:72px;margin:0 auto;" alt="brasbug" src="http://www.flywithme.top/public/coderjk.jpg"></img>
              Fly With Me
            </a>
          </h1>
          <p class="site-description"> 漫步走在沙滩上 </p>
        </div>

      </div>
    </div>
    
  </div>
  

</header>



  <div class="container" id="page">
    <div class="container-inner">
      <div class="main">
        <div class="main-inner group">
          <section class="content">
            <div class="page-title pad group">
              <ul class="meta-single group">
                
                <li class="category">
                  <a href="http://www.flywithme.top/categories/%E6%8A%80%E6%9C%AF%E6%96%87%E7%AB%A0/" rel="category tag">技术文章</a>
                </li>
                
              </ul>
            </div>

            <div class="pad group">

              <article
                  class="post type-post status-publish format-standard has-post-thumbnail hentry category-australien tag-bondi-beach tag-city2surf tag-sydney">
                <div class="post-inner group">

                  <h1 class="post-title">React Native如何做跨平台设计</h1>

                  <p class="post-byline">
                    by  · 2016年04月28日 · 3136 Words ·
                    ~7min reading time |
                    <a href="https://github.com/brasbug/brasbug-hugo-blog/tree/master/content/post/2016/2016-04-28-ReactNativeRD.md"
                       target="_blank">Improve on <i class="fa fa-github"></i></a>
                  </p>

                  <div class="clear"></div>

                  <div class="entry">
                    <div class="entry-inner">
                      
                      

<h5 id="facebook-开源">Facebook 开源</h5>

<blockquote>
<p>Facebook 在它的F8开发者大会上开源了它的F8大会App，甚至还配上了教程，我组织了一个翻译小队对这个教程进行了翻译。虽然比百万哥晚了几天，但总算还是完成了，欢迎大家Review。项目地址：</p>

<p><a href="https://github.com/brasbug/Building-the-F8-2016-App-CN/">https://github.com/brasbug/Building-the-F8-2016-App-CN/</a></p>

<p>本文出自教程第二篇，介绍React Native在设计跨平台应用时的一些原则和技巧。</p>
</blockquote>

<p>React Native 的一大优势是：可以只用一种语法编写分别运行在 iOS 和 Android 平台上的程序，且可重用部分应用逻辑。</p>

<p>然而，与“一次编写，到处运行”的理念不同的是，React Native 的哲学是“一次学习，到处编写”。如此一来，即使用 React Native 编写不同平台的程序，也可以尽可能贴合每个平台的特性。</p>

<p>从 UI 的角度来看，每个平台都有自己独特的视觉风格、UI 范例甚或是技术层面的功能，那我们设计出一个统一的 UI 基础组件，然后再按照各平台特性进行调整岂不乐乎？</p>

<h6 id="准备工作">准备工作</h6>

<p>在后续的所有教程中，我们会仔细解读 App 的源代码，请克隆一份源代码到本地可访问的路径，然后根据配置说明在本地运行 App。在本章的教程中，你只需要阅读相关源代码。源代码地址：</p>

<p><a href="https://github.com/brasbug/f8app">https://github.com/brasbug/f8app</a></p>

<h6 id="react-native-思维模式">React Native 思维模式</h6>

<p>在你写任何 React 代码之前，请认真思考这个至关重要的问题：<strong>如何才能尽可能多地重用代码?</strong></p>

<p>React Native 的理念是针对每个平台分而治之，代码重用的做法看起来与之相违背，好像我们就应该为每个平台定制其专属的视觉组件一样，但实际上我们仍需努力让每个平台上的代码尽可能多地统一。</p>

<p>构建一套 React Native 应用视觉组件的关键点在于如何最好地实现平台抽象。开发人员和设计师可以列出应用中需要重用的组件，例如按钮、容器、列表行，头部等等，只有在必要的时候才单独为每个平台设计特定的组件。</p>

<p>当然，有一些组件相对于其它组件而言更为复杂，我们先一起来看看 F8 应用中不同的组件有什么区别。</p>

<h6 id="小组件">小组件</h6>

<p>请看 F8 应用的示例图：
<img src="sources/reactnative/reactnativeiOSAndroid.png" alt="react native iOSAndroid.png" /></p>

<p>在 iOS 版本中，我们用 iOS 系统中很常见的圆角边框风格来切分 Tab 控制；在 Android 版本中，我们用下划线的风格来标示这个组件。而这两个控制组件的功能其实完全相同。</p>

<p>所以，即使两者样式稍有不同，但是实现的功能相同，所以我们可以用同一套代码抽象此处的逻辑，从而可以尽可能多地重用代码。</p>

<p>我们针对像这样的小组件做了很多跨平台重用逻辑代码的案例，比如一个简单的文本按钮，在每个平台上我们都会设计不同的 hover 和 active 状态的样式，但是除开这些视觉上的细微的差异外，逻辑功能完全相同。所以我们总结了一个抽象 React Native 视觉组件的最佳实践方法：设计一套相同的逻辑代码，然后在控制语句中编写其余需要差异化的部分。</p>

<p>以下是这个组件的示例代码（来自 <F8SegmentedControl>）：</p>

<pre><code>class Segment extends React.Component {
  props: {
    value: string;
    isSelected: boolean;
    selectionColor: string;
    onPress: () =&gt; void;
  };

  render() {
    var selectedButtonStyle;
    if (this.props.isSelected) {
      selectedButtonStyle = { borderColor: this.props.selectionColor };
    }
    var deselectedLabelStyle;
    if (!this.props.isSelected &amp;&amp; Platform.OS === 'android') {
      deselectedLabelStyle = styles.deselectedLabel;
    }
    var title = this.props.value &amp;&amp; this.props.value.toUpperCase();

    var accessibilityTraits = ['button'];
    if (this.props.isSelected) {
      accessibilityTraits.push('selected');
    }

    return (
      &lt;TouchableOpacity
        accessibilityTraits={accessibilityTraits}
        activeOpacity={0.8}
        onPress={this.props.onPress}
        style={[styles.button, selectedButtonStyle]}&gt;
        &lt;Text style={[styles.label, deselectedLabelStyle]}&gt;
          {title}
        &lt;/Text&gt;
      &lt;/TouchableOpacity&gt;
    );
  }
}
</code></pre>

<p>在这段代码中，我们为每一种平台分别应用了不同的样式（用到了 React Native 的 <strong>Platform 模块</strong>）。各平台中的 Tab 按钮都应用了相同的通用样式，同时也根据各平台特性定制了独占样式（同样出自 <F8SegmentedControl>）：</p>

<pre><code>var styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    backgroundColor: 'transparent',
    ios: {
      paddingBottom: 6,
      justifyContent: 'center',
      alignItems: 'center',
    },
    android: {
      paddingLeft: 60,
    },
  },
  button: {
    borderColor: 'transparent',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'transparent',
    ios: {
      height: HEIGHT,
      paddingHorizontal: 20,
      borderRadius: HEIGHT / 2,
      borderWidth: 1,
    },
    android: {
      paddingBottom: 6,
      paddingHorizontal: 10,
      borderBottomWidth: 3,
      marginRight: 10,
    },
  },
  label: {
    letterSpacing: 1,
    fontSize: 12,
    color: 'white',
  },
  deselectedLabel: {
    color: 'rgba(255, 255, 255, 0.7)',
  },
});    
</code></pre>

<p>在这段代码中我们使用了一个改编自 <strong>React Native StyleSheet API</strong> 的函数 F8StyleSheet，它可以针对各平台分别进行样式转换操作：</p>

<pre><code>export function create(styles: Object): {[name: string]: number} {
  const platformStyles = {};
  Object.keys(styles).forEach((name) =&gt; {
    let {ios, android, ...style} = {...styles[name]};
    if (ios &amp;&amp; Platform.OS === 'ios') {
      style = {...style, ...ios};
    }
    if (android &amp;&amp; Platform.OS === 'android') {
      style = {...style, ...android};
    }
    platformStyles[name] = style;
  });
  return StyleSheet.create(platformStyles);
}
</code></pre>

<p>在这个 F8StyleSheet 函数中我们解析了前面示例代码中的 styles 对象，如果我们发现了匹配当前平台的 ios 或 android键值，就会应用相应的样式，如果都没有，则应用默认样式。以此看来，减少代码重复的另一种做法是：尽可能多地重用通用样式代码。</p>

<p>现在，我们已经可以在我们的App中重用这个组件了，它也可以根据不同的平台自动匹配相应的样式。</p>

<h6 id="分离复杂差异">分离复杂差异</h6>

<p>如果一个组件在各平台上的差异不仅仅是样式的不同，也存在大量的逻辑代码差异，那我们就需要换一种方式了。正如下图所示，iOS 和 Android 平台中最高阶的菜单导航组件就有非常大的差异：</p>

<p><img src="sources/reactnative/reactnativeiOSAndroid02.png" alt="react native02" /></p>

<p>正如你所见，在 iOS 版本中我们在屏幕底部放了一个固定的 Tab，而在 Android 版本中，我们却实现了一种可划出的侧边栏。这两种组件其实是本质上的不同，况且一般来说，在 Android 应用中，这种侧边栏通常还会包含更多的菜单选项，例如：退出登录。</p>

<p>你当然可以将这两种菜单模式写到一个组件中去，但是这个组件会变得异常臃肿，所有的差异不得不通过大量的分支语句来实现，你一定会在不久的将来对这段代码感到陌生难懂。</p>

<p>其实，我们可以用 React Native 内建的平台特定的扩展来解决这个问题。我们可以创建两个独立的应用，在下面的示例中我们会创建两个组件，分别命名为：<code>F8TabsView.ios.js</code> 和 <code>F8TabsView.android.js</code>。React Native 会自动检测当前平台并根据扩展命名加载相应的组件。</p>

<h6 id="内建ui组件">内建UI组件</h6>

<p>在每一个 FBTabsView 组件中，我们也可以重用一些内建的 React Native UI 组件，Android 版本使用的是<code>DrawerLayoutAndroid</code>（很显然，只在 Android 中可用）：</p>

<pre><code>render(){
 return(
   &lt;DrawerLayoutAndroid
     ref=&quot;drawer&quot;
     drawerWidth={300}
     drawerPosition={DrawerLayoutAndroid.positions.Left}
     renderNavigationView={this.renderNavigationView}&gt;
     &lt;View style={style.content} key={this.props.activeTab}&gt;
     {this.renderContent()}
     &lt;View&gt;
     &lt;/DrawerLayoutAndroid&gt;
 );
}     
</code></pre>

<p>在第8行代码中，我们在当前的类中显式地为 drawer 组件指定了 renderNavigationView() 函数。这个函数会返回 drawer 中渲染出来的内容。在这个示例中，我们渲染的是一个包含在自定义 MenuItem 组件(点击查看 <code>MenuItem.js</code>)中的 <code>View</code> 组件：</p>

<pre><code>renderNavigationView() {
...
 return (
      &lt;View style={styles.drawer}&gt;
        &lt;Image
          style={styles.header}
          source={require('./img/drawer-header.png')}&gt;
          {accountItem}
        &lt;/Image&gt;
        &lt;MenuItem
          title=&quot;Schedule&quot;
          selected={this.props.tab === 'schedule'}
          onPress={this.onTabSelect.bind(this, 'schedule')}
          icon={scheduleIcon}
          selectedIcon={scheduleIconSelected}
        /&gt;
        {myF8Item}
        &lt;MenuItem
          title=&quot;Maps&quot;
          selected={this.props.tab === 'map'}
          onPress={this.onTabSelect.bind(this, 'map')}
          icon={require('./maps/img/maps-icon.png')}
          selectedIcon={require('./maps/img/maps-icon-active.png')}
        /&gt;
        &lt;MenuItem
          title=&quot;Notifications&quot;
          selected={this.props.tab === 'notifications'}
          onPress={this.onTabSelect.bind(this, 'notifications')}
          badge={this.props.notificationsBadge}
          icon={require('./notifications/img/notifications-icon.png')}
          selectedIcon={require('./notifications/img/notifications-icon-active.png')}
        /&gt;
        &lt;MenuItem
          title=&quot;Info&quot;
          selected={this.props.tab === 'info'}
          onPress={this.onTabSelect.bind(this, 'info')}
          icon={require('./info/img/info-icon.png')}
          selectedIcon={require('./info/img/info-icon-active.png')}
        /&gt;
        {loginItem}
      &lt;/View&gt;
 );
}
</code></pre>

<p>相比之下，iOS 版本直接在 render() 函数中使用了一个不同的内建组件，<code>TabBarIOS</code>：</p>

<pre><code>render() {
    var scheduleIcon = this.props.day === 1
      ? require('./schedule/img/schedule-icon-1.png')
      : require('./schedule/img/schedule-icon-2.png');
    var scheduleIconSelected = this.props.day === 1
      ? require('./schedule/img/schedule-icon-1-active.png')
      : require('./schedule/img/schedule-icon-2-active.png');
    return (
      &lt;TabBarIOS tintColor={F8Colors.darkText}&gt;
        &lt;TabBarItemIOS
          title=&quot;Schedule&quot;
          selected={this.props.tab === 'schedule'}
          onPress={this.onTabSelect.bind(this, 'schedule')}
          icon={scheduleIcon}
          selectedIcon={scheduleIconSelected}&gt;
          &lt;GeneralScheduleView
            navigator={this.props.navigator}
            onDayChange={this.handleDayChange}
          /&gt;
        &lt;/TabBarItemIOS&gt;
        &lt;TabBarItemIOS
          title=&quot;My F8&quot;
          selected={this.props.tab === 'my-schedule'}
          onPress={this.onTabSelect.bind(this, 'my-schedule')}
          icon={require('./schedule/img/my-schedule-icon.png')}
          selectedIcon={require('./schedule/img/my-schedule-icon-active.png')}&gt;
          &lt;MyScheduleView
            navigator={this.props.navigator}
            onJumpToSchedule={() =&gt; this.props.onTabSelect('schedule')}
          /&gt;
        &lt;/TabBarItemIOS&gt;
        &lt;TabBarItemIOS
          title=&quot;Maps&quot;
          selected={this.props.tab === 'map'}
          onPress={this.onTabSelect.bind(this, 'map')}
          icon={require('./maps/img/maps-icon.png')}
          selectedIcon={require('./maps/img/maps-icon-active.png')}&gt;
          &lt;F8MapView /&gt;
        &lt;/TabBarItemIOS&gt;
        &lt;TabBarItemIOS
          title=&quot;Notifications&quot;
          selected={this.props.tab === 'notifications'}
          onPress={this.onTabSelect.bind(this, 'notifications')}
          badge={this.props.notificationsBadge || null}
          icon={require('./notifications/img/notifications-icon.png')}
          selectedIcon={require('./notifications/img/notifications-icon-active.png')}&gt;
          &lt;F8NotificationsView navigator={this.props.navigator} /&gt;
        &lt;/TabBarItemIOS&gt;
        &lt;TabBarItemIOS
          title=&quot;Info&quot;
          selected={this.props.tab === 'info'}
          onPress={this.onTabSelect.bind(this, 'info')}
          icon={require('./info/img/info-icon.png')}
          selectedIcon={require('./info/img/info-icon-active.png')}&gt;
          &lt;F8InfoView navigator={this.props.navigator} /&gt;
        &lt;/TabBarItemIOS&gt;
      &lt;/TabBarIOS&gt;
    );
  }
</code></pre>

<p>显而易见，尽管 iOS 菜单接受了相同的数据，但是它的结构略有不同。我们并没有用一个独立的函数创建菜单元素，而是将这些元素作为父级菜单的子元素插入进来，正如 <code>TabBarItemIOS</code> 组件这样。 这里的 <code>TabBarItem</code> 与 <code>Android</code> 中 的 <code>MenuItem</code> 本质上是相同的，唯一的区别是在 <code>Android</code> 组件中我们会定义一个独立的主<strong>View 组件</strong>：</p>

<pre><code>&lt;View 
  style={styles.content} 
  key={this.props.activeTab}&gt;
  {this.renderContent()}
&lt;/View&gt;
</code></pre>

<p>然后当一个 tab 改变时改变这个组件（通过 <code>renderContent()</code> 函数），而 iOS 组件则会有多个分离的 View 组件，例如：</p>

<pre><code>&lt;GeneralScheduleView
  navigator={this.props.navigator}
  onDayChange={this.handleDayChange}
/&gt;
</code></pre>

<p>这是 TabBarItem 的一部分，可以点击使它们可见。</p>

<h6 id="设计迭代周期">设计迭代周期</h6>

<p>当你构建任何应用，无论是在移动平台还是 web 环境下，调整适配的 UI 元素是非常痛苦的。如果工程师和设计师共同协作，会使整个过程慢下来。</p>

<p>React Native 包含了一个<strong>实时重载</strong>的 debug 功能，可以当 JavaScript 改变时触发刷新应用。这可以在极大程度上减少设计迭代过程，一旦改变了组件样式并保存后，你会立即看到更新的样式。</p>

<p>但是如果组件在不同条件下看起来不同怎么办呢？举个例子，一个按钮组件可能有一个默认样式，也分别包含按下、执行任务中、执行任务完成时的样式。</p>

<p>为了避免每次都与应用交互，我们内建了一个用于 debug 视觉效果的 Playgroud 组件：</p>

<pre><code>class Playground extends React.Component {
  constructor() {
    super();
    const content = [];
    const define = (name: string, render: Function) =&gt; {
      content.push(&lt;Example key={name} render={render} /&gt;);
    };
    // var Module = require('F8PageControl');
    var Module = require('F8Header');
    // var Module = require('./tabs/schedule/AddToScheduleButton');
    // var Module = require('./rating/Header');
    Module.__cards__(define);
    this.state = {content};
  }

  render() {
    return (
      &lt;View style={{backgroundColor: '#336699', flex: 1,}}&gt;
        {this.state.content}
      &lt;/View&gt;
    );
  }
}
</code></pre>

<p>其实我们只是创建了一个可交换加载的空视图，将其与一些示例定义整合到其中一个 UI 组件中，正如下面这段<code>AddToScheduleButton.js</code> 所示：</p>

<pre><code>module.exports = AddToScheduleButton;
module.exports.__cards__ = (define) =&gt; {
  let f;
  setInterval(() =&gt; f &amp;&amp; f(), 1000);

  define('Inactive', (state = true, update) =&gt;
    &lt;AddToScheduleButton isAdded={state} onPress={() =&gt; update(!state)} /&gt;);

  define('Active', (state = false, update) =&gt;
    &lt;AddToScheduleButton isAdded={state} onPress={() =&gt; update(!state)} /&gt;);

  define('Animated', (state = false, update) =&gt; {
    f = () =&gt; update(!state);
    return &lt;AddToScheduleButton isAdded={state} /&gt;;
  });
};
</code></pre>

<p>我们可以将这个应用转化为一个 UI 预览工具：</p>

<p><img src="sources/reactnative/reactnativeiOSAndroid3.gif" alt="react native03" /></p>

<p>在这个示例中为这个按钮定义了按下和抬起两种状态，第三个按钮在这两者的状态之间不断循环，以此来预览过渡的动画效果。</p>

<p>现在，我们可以跟设计师一起快速调整基础组件的视觉样式了。</p>

<p>如果想用这个功能，<code>&lt;Playground&gt;</code>组件必须在任何 React Native 应用中都可用，我们需要在 setup() 函数中交换一些代码来加载 <Playground> 组件：</p>

<pre><code>/* from js/setup.js */
render() {  
  ...
  return (
    &lt;Provider store={this.state.store}&gt;
      &lt;F8App /&gt;
    &lt;/Provider&gt;
  );
}

变为

/* in js/setup.js */
render() {
  ...
  return (
    &lt;Provider store={this.state.store}&gt;
      &lt;Playground /&gt;
    &lt;/Provider&gt;
  );
}
</code></pre>

<p>当然，你也可以修改 <Playground> 组件，使其能够改变引入的其它组件。</p>

<p><a href="https://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&amp;mid=2651112134&amp;idx=1&amp;sn=c26ceb682e361ae33bc1eb5348e5e7bb&amp;scene=1&amp;srcid=0427SeJXKKZSAvVR2M52qOsw&amp;key=b28b03434249256ba8ea0cb4d56d79d314519c90f31a529f8d632996958f4eaca1cbdad191c223497b256c6ceea8391c&amp;ascene=0&amp;uin=MTg5NzM2MjkyMA%3D%3D&amp;devicetype=iMac+MacBookPro11%2C5+OSX+OSX+10.11.4+build(15E65)&amp;version=11020201&amp;pass_ticket=Nu5nSZlfui6B7G7DcB%2F6TCakJdlOAf3o9ND1eoSVkm2wZBUGQjsZFgKkrsou%2Bk4%2B">转载 From</a></p>

                      
                    </div>
                    <div style="border: 1px dashed #e0e0e0; padding: 10px 10px 10px 10px; background-color: #fffeee; background-repeat: no-repeat; background-attachment: scroll; background-position: 1% 50%; -moz-background-size: auto auto; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
                        <div style="float:left;margin-top:0px;">
                        <img src="http://www.flywithme.top/public/qrcode.jpg" width="129px" height="129px"/>
                        <div style="text-align:center;">微信扫一扫</div>
                        </div>
                        <div>
                            <p style="margin-top:0px;">作者：<a target="_blank" href="http://blog.flywithme.top/">Brasbug</a>
                            <br />微信关注：Crime_Sence (我在案发现场）
                            <br />本文出处：<a target="_blank" href="http://www.flywithme.top/2016/04/28/ReactNativeRD/">http://www.flywithme.top/2016/04/28/ReactNativeRD/</a>
                            <br />
                            文章版权归本人所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。 </p>
                        </div>
                    </div>
                    <div class="clear"></div>
                  </div>
                  

                </div>
                
              </article>
              

              <div class="clear"></div>

              <p class="post-tags">
                <span>Tags:</span>
                
              </p>

              <h4 class="heading">
  <i class="fa fa-hand-o-right"></i>Related posts</h4>

    <ul class="related-posts group">
    
    
    
        
        
        
    
        
        
        
    
        
        
        
    
        
        
        
    
        
        
        
    
    </ul>



              <section id="comments" class="themeform">
<div class="ds-thread" data-thread-key="/2016/04/28/ReactNativeRD/" data-title="React Native如何做跨平台设计" data-url="http://www.flywithme.top/2016/04/28/ReactNativeRD/"></div>
<script type="text/javascript">
var duoshuoQuery = {short_name:"brasbug"};
	(function() {
		var ds = document.createElement('script');
		ds.type = 'text/javascript';ds.async = true;
		ds.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//static.duoshuo.com/embed.js';
		ds.charset = 'UTF-8';
		(document.getElementsByTagName('head')[0] 
		 || document.getElementsByTagName('body')[0]).appendChild(ds);
	})();
</script>
</section>


              
            </div>


          </section>
          <div class="sidebar s1">
  <a class="sidebar-toggle" title="Expand Sidebar"><i class="fa icon-sidebar-toggle"></i></a>
  <div class="sidebar-content">
    <div class="sidebar-top group">
      <p>Follow:</p>
      <ul class="social-links">
    <li>
    <a class="social-tooltip" title="On WeiBo"
        href="http://weibo.com/u/2144561657?refer_flag=1005055013_&is_all=1" target="_blank">
        <i class="fa fa-weibo"></i>
    </a>
    </li>
    
    
        
        
    
    
    <li>
    <a class="social-tooltip" title="On GitHub"
        href="https://github.com/brasbug" target="_blank">
        <i class="fa fa-github"></i>
    </a>
    </li>
</ul>

    </div>
    <div class="widget qrcode">
    <img src="http://www.flywithme.top/public/qrcode.jpg" alt="qrcode" width="172px" height="172px"/>
    <p> 微信扫一扫：<br/> 关注我的公众号： <br /> Crime_Scene <br /> 交流QQ群：185198503 </p>
</div>

    <ul class="post-nav group">
      <li class="next">
        
        <a href="http://www.flywithme.top/2016/04/27/Multipeer-Connectivity/" rel="next">
          <i class="fa fa-chevron-right"></i>
          <strong>Next post</strong>
          <span>利用Multipeer Connectivity框架进行WiFi传输</span>
        </a>
        
      </li>
      <li class="previous">
        
        <a href="http://www.flywithme.top/2016/05/03/UINavigationBar-Summary/" rel="prev">
          <i class="fa fa-chevron-left"></i>
          <strong>Previous Post</strong>
          <span>UINavigationBar-使用总结</span>
        </a>
        
      </li>
    </ul>
    <div id="search-2" class="widget widget_search"><h3>Search</h3>
      <form method="get" class="searchform themeform" action="https://www.google.com/search">
        <div>
          <input type="text" class="search" name="q" placeholder="Press enter to start searching">
        </div>
      </form>
    </div>
  </div>
  
</div>

          <div class="sidebar s2">
  <a class="sidebar-toggle" title="Expand Sidebar"><i class="fa icon-sidebar-toggle"></i></a>
  <div class="sidebar-content">
    <div class="sidebar-top group">
      <p>More</p>
    </div>
    <div id="categories-2" class="widget widget_categories"><h3>Categories</h3>
      <ul>
        
          <li class="cat-item cat-item-1">
            <a rel="nofollow" href="http://www.flywithme.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
          </li>
        
          <li class="cat-item cat-item-1">
            <a rel="nofollow" href="http://www.flywithme.top/categories/%E6%8A%80%E6%9C%AF%E6%96%87%E7%AB%A0/">技术文章</a>
          </li>
        
          <li class="cat-item cat-item-1">
            <a rel="nofollow" href="http://www.flywithme.top/categories/%E6%80%9D%E8%80%83%E6%84%9F%E6%82%9F/">思考感悟</a>
          </li>
        
          <li class="cat-item cat-item-1">
            <a rel="nofollow" href="http://www.flywithme.top/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
          </li>
        
      </ul>
    </div>
  </div>
</div>

        </div>
      </div>
    </div>
  </div>
  <footer id="footer">

  <nav class="nav-container group" id="nav-footer">
    <div class="nav-toggle"><i class="fa fa-bars"></i></div>
    <div class="nav-text"></div>
    <div class="nav-wrap">
      <ul id="menu-default-menu-1" class="nav container group">
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/">首页</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E6%8A%80%E6%9C%AF%E6%96%87%E7%AB%A0/">技术文章</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E6%80%9D%E8%80%83%E6%84%9F%E6%82%9F/">思考感悟</a>
        </li>
        
        <li class="menu-item menu-item-type-post_type menu-item-object-page">
          <a rel="nofollow" href="http://www.flywithme.top/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
        </li>
        
      </ul>
    </div>
  </nav>
  

  <section class="container" id="footer-bottom">
    <div class="container-inner">
      <a id="back-to-top" href="#"><i class="fa fa-angle-up"></i></a>
      <div class="pad group">
        <div class="grid one-half">
          <div id="copyright">
            <p>Copyright © 2015. All Rights Reserved. <br>Powered by
              <a rel="nofollow" href="http://gohugo.io/" target="_blank">Hugo - the static site generator</a>.
              <a rel="nofollow" href="http://golang.org" target="_blank">#golang</a>.
            </p>
          </div>
          
        </div>
        
        <div class="grid one-half last">
          <ul class="social-links">
    <li>
    <a class="social-tooltip" title="On WeiBo"
        href="http://weibo.com/u/2144561657?refer_flag=1005055013_&is_all=1" target="_blank">
        <i class="fa fa-weibo"></i>
    </a>
    </li>
    
    
        
        
    
    
    <li>
    <a class="social-tooltip" title="On GitHub"
        href="https://github.com/brasbug" target="_blank">
        <i class="fa fa-github"></i>
    </a>
    </li>
</ul>

        </div>
      </div>

    </div>
    
  </section>
  

</footer>

</div>
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-10147768-2', 'auto');
  ga('send', 'pageview');

  $('#messagesbsb').remove();

</script>
</body>
</html>

